/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2020. All rights reserved.
 * secGear is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#include <unistd.h>
#include <stdlib.h>
#include "tstenclave_t.h"
#include <string.h>

#define MAX_WORD_SIZE 16

typedef struct TSTNode {
    char data[MAX_WORD_SIZE];
    struct TSTNode *left;
    struct TSTNode *middle;
    struct TSTNode *right;
} TSTNode;

TSTNode *createTSTNode(char *data) {
    TSTNode *newNode = (TSTNode*) malloc(sizeof(TSTNode));
    strncpy(newNode->data, data, MAX_WORD_SIZE);
    newNode->left = NULL;
    newNode->middle = NULL;
    newNode->right = NULL;
    return newNode;
}

TSTNode *insertTST(TSTNode *root, char *data) {
    if (!root) {
        root = createTSTNode(data);
    } else if (strcmp(data, root->data) < 0) {
        root->left = insertTST(root->left, data);
    } else if (strcmp(data, root->data) > 0) {
        root->right = insertTST(root->right, data);
    } else {
        root->middle = insertTST(root->middle, data);
    }
    return root;
}

TSTNode *searchTST(TSTNode *root, char *data) {
    if (!root) {
        return NULL;
    } else if (strcmp(data, root->data) < 0) {
        return searchTST(root->left, data);
    } else if (strcmp(data, root->data) > 0) {
        return searchTST(root->right, data);
    } else {
        return root;
    }
}

size_t calculateTSTSize(TSTNode* root) {
    if (!root) {
        return 0;
    }
    size_t size = sizeof(TSTNode);
    size += calculateTSTSize(root->left);
    size += calculateTSTSize(root->middle);
    size += calculateTSTSize(root->right);
    return size;
}



typedef struct binaryNode {
    char data[MAX_WORD_SIZE];
    // char *data;
    struct binaryNode *left;
    struct binaryNode *right;
} BinaryNode;

BinaryNode *createBinaryNode(char *data) {
    BinaryNode *newNode = (BinaryNode*) malloc(sizeof(BinaryNode));
    strncpy(newNode->data, data, MAX_WORD_SIZE);
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

BinaryNode *insertBinary(BinaryNode *root, char *data) {
    if (!root) {
        root = createBinaryNode(data);
    } else if (strcmp(data, root->data) < 0) {
        root->left = insertBinary(root->left, data);
    } else {
        root->right = insertBinary(root->right, data);
    }
    return root;
}

BinaryNode *searchBinary(BinaryNode *root, char *data) {
    if (!root) {
        return NULL;
    } else if (strcmp(data, root->data) < 0) {
        return searchBinary(root->left, data);
    } else if (strcmp(data, root->data) > 0) {
        return searchBinary(root->right, data);
    } else {
        return root;
    }
}
size_t bin_size = 0;
size_t calculateBinarySize(BinaryNode* root) {
    if (!root) {
        return 0;
    }
    /*
    if (root->left == NULL && root->right == NULL) {

        return sizeof(BinaryNode);
    }
    // size_t size = sizeof(BinaryNode);
    if (root->left) {
        bin_size += calculateBinarySize(root->left);
    }
    if (root->right) {
        bin_size += calculateBinarySize(root->right);
    }
    return bin_size;
    */

    return (calculateBinarySize(root->left) + sizeof(BinaryNode) + calculateBinarySize(root->right));
}

typedef struct trieNode {
    char data;
    int isEndOfWord;
    struct trieNode *children[26];
} TrieNode;

TrieNode *createTrieNode(char data) {
    TrieNode *newNode = (TrieNode*) malloc(sizeof(TrieNode));
    newNode->data = data;
    newNode->isEndOfWord = 0;
    for (int i = 0; i < 26; i++) {
        newNode->children[i] = NULL;
    }
    return newNode;
}

TrieNode *insertTrie(TrieNode *root, char *data) {
    if (!root) {
        root = createTrieNode(*data);
    }
    if (*data < root->data) {
        root->children[*data - 'a'] = insertTrie(root->children[*data - 'a'], data);
    } else if (*data > root->data) {
        root->children[*data - 'a'] = insertTrie(root->children[*data - 'a'], data);
    } else {
        if (*(data + 1)) {
            root->children[*data - 'a'] = insertTrie(root->children[*data - 'a'], data + 1);
        } else {
            root->isEndOfWord = 1;
        }
    }
    return root;
}

TrieNode *searchTrie(TrieNode *root, char *data) {
    if (!root) {
        return NULL;
    }
    if (*data < root->data) {
        return searchTrie(root->children[*data - 'a'], data);
    } else if (*data > root->data) {
        return searchTrie(root->children[*data - 'a'], data);
    } else {
        if (*(data + 1)) {
            return searchTrie(root->children[*data - 'a'], data + 1);
        } else {
            if (root->isEndOfWord) {
                return root;
            } else {
                return NULL;
            }
        }
    }
}

size_t calculateTrieSize(TrieNode* root) {
    if (!root) {
        return 0;
    }
    size_t size = sizeof(TrieNode);
    for (int i = 0; i < 26; i++) {
        size += calculateTrieSize(root->children[i]);
    }
    return size;
}

TSTNode *tstRoot = NULL;
BinaryNode *binaryRoot = NULL;
TrieNode *trieRoot = NULL;

char buf[16];
uint32_t siz_buf[8];

void insertTST_(char *token){
    strncpy(buf, token, sizeof(buf));
    tstRoot = insertTST(tstRoot, buf);
}

void insertBinary_(char *token){
    strncpy(buf, token, sizeof(buf));
    binaryRoot = insertBinary(binaryRoot, buf);
}

void insertTrie_(char *token){
    strncpy(buf, token, sizeof(buf));
    trieRoot = insertTrie(trieRoot, buf);
}

void calc_result(){
    siz_buf[0]=calculateTSTSize(tstRoot);
    siz_buf[1]=calculateBinarySize(binaryRoot);
    siz_buf[2]=calculateTrieSize(trieRoot);
    siz_buf[3]=233;
}

void get_result(uint32_t *dst){
    memcpy(dst, siz_buf, sizeof siz_buf);
}

int searchTST_(char *token){
    strncpy(buf, token, sizeof(buf));
    // return calculateTSTSize(tstRoot);
    return searchTST(tstRoot, buf)?1:0;
}

int searchBinary_(char *token){
    strncpy(buf, token, sizeof(buf));
    // return calculateBinarySize(binaryRoot);
    return searchBinary(binaryRoot, buf)?1:0;
}

int searchTrie_(char *token){
    strncpy(buf, token, sizeof(buf));
    // return calculateTrieSize(trieRoot);
    return searchTrie(trieRoot, buf)?1:0;
}
size_t sizeTST_(char * token)
{
    strncpy(buf, token, sizeof(buf));
    return calculateTSTSize(tstRoot);
}
size_t sizeBinary_(char *token)
{
    strncpy(buf, token, sizeof(buf));
    return calculateBinarySize(binaryRoot);
}
size_t sizeTrie_(char *token) 
{
    strncpy(buf, token, sizeof(buf));
    return calculateTrieSize(trieRoot);
}
size_t sizeTest_(char *token) 
{
    strncpy(buf, token, sizeof(buf));
    return 42;
}